﻿using System;
using System.Collections.Generic;
using System.Text;

namespace PostProcessing
{
    public class RuleGroups
    {
        /// <summary>
        /// Agrupa por Taxonomias e Numeros de Antecedentes.
        /// </summary>
        public Dictionary<string, Dictionary<string, List<Rule>>> regrasAgrupadas;
        List<Taxonomy> taxonomias;

        public RuleGroups(List<Taxonomy> taxonomias)
        {
            regrasAgrupadas = new Dictionary<string, Dictionary<string, List<Rule>>>();
            regrasAgrupadas.Add("?", new Dictionary<string, List<Rule>>());
            this.taxonomias = taxonomias;
        }

        void addTaxonomySubGroup(string nome)
        {
            regrasAgrupadas.Add(nome, new Dictionary<string,List<Rule>>());
        }

        void addNumberSubGroup(string taxonomySubGroup, string number)
        {
            regrasAgrupadas[taxonomySubGroup].Add(number, new List<Rule>());
        }

        /// <summary>
        /// Adiciona uma determinada regra no grupo de taxonomias genérico.
        /// </summary>
        /// <param name="regra"></param>
        /// <param name="indexAntecedente"></param>
        public void addRow(Rule regra)
        {
            string numAntec = regra.AntecedenteDesmembrado.Count.ToString();

            //Se subgrupo de numero de antecedentes nao existe, cria-o.
            if(!regrasAgrupadas["?"].ContainsKey(numAntec))
                addNumberSubGroup("?", numAntec);

            regrasAgrupadas["?"][numAntec].Add(regra);
        }

        void addGroupedRow(Rule regra, string taxGroup, string numGroup)
        {
            if (!regrasAgrupadas.ContainsKey(taxGroup))
                regrasAgrupadas.Add(taxGroup, new Dictionary<string,List<Rule>>());

            if (!regrasAgrupadas[taxGroup].ContainsKey(numGroup))
                regrasAgrupadas[taxGroup].Add(numGroup, new List<Rule>());

            regrasAgrupadas[taxGroup][numGroup].Add(regra);
        }

        /// <summary>
        /// Retira as regras do subgrupo de taxonomia genérico, e coloca-as em suas devidas posições de acordo com a taxonomia do antecedente especificado.
        /// </summary>
        /// <param name="indexAntecedente"></param>
        public void reagruparPorAntecedente(int indexAntecedente)
        {
            string taxSubGroupAtual;
            string numAntecsAtual;
            Rule regraAtual;
            Dictionary<Rule, List<string>> regrasParaReadicionar = new Dictionary<Rule, List<string>>();

            //Para cada grupo de taxonomias...
            foreach (KeyValuePair<string, Dictionary<string, List<Rule>>> taxonomySubGroup in regrasAgrupadas)
            {
                //Para cada subgrupo de numeros de antecedentes...
                foreach (KeyValuePair<string, List<Rule>> numberSubGroup in taxonomySubGroup.Value)
                {
                    //Para cada regra existente neste subgrupo...
                    for (int i = 0; i < numberSubGroup.Value.Count; i++)
                    {
                        regraAtual = numberSubGroup.Value[i];
                        numAntecsAtual = regraAtual.AntecedenteDesmembrado.Count.ToString();
                        taxSubGroupAtual = encontraTaxonomia(regraAtual, indexAntecedente);

                        if (taxSubGroupAtual == null)
                            taxSubGroupAtual = "?";

                        //Se esta regra esta no grupo de taxonomias errado...
                        if (taxSubGroupAtual != taxonomySubGroup.Key)
                        {
                            numberSubGroup.Value.RemoveAt(i);
                            i--;

                            regrasParaReadicionar.Add(regraAtual, new List<string>(){taxSubGroupAtual, numAntecsAtual});
                        }
                    }
                }
            }

            //Adiciona as regras em suas novas posições.
            foreach (KeyValuePair<Rule, List<string>> regraMudar in regrasParaReadicionar)
            {
                addGroupedRow(regraMudar.Key, regraMudar.Value[0], regraMudar.Value[1]);
            }
            regrasParaReadicionar.Clear();

            limparEstrutura();
        }

        /// <summary>
        /// Retira os objetos vazios da estrutura de agrupamento de regras.
        /// </summary>
        void limparEstrutura() 
        {
            List<string> numSubGrpParaRemover = new List<string>();
            List<string> taxSubGrpParaRemover = new List<string>();

            foreach (KeyValuePair<string, Dictionary<string, List<Rule>>> taxonomySubGroup in regrasAgrupadas)
            {
                foreach (KeyValuePair<string, List<Rule>> numberSubGroup in taxonomySubGroup.Value)
                {
                    if (numberSubGroup.Value.Count == 0)
                        numSubGrpParaRemover.Add(numberSubGroup.Key);
                }

                //Remove os subgrupos de numeros que estao vazios.
                foreach (string numSubGrp in numSubGrpParaRemover)
                {
                    taxonomySubGroup.Value.Remove(numSubGrp);
                }
                numSubGrpParaRemover.Clear();

                if (taxonomySubGroup.Value.Count == 0)
                    taxSubGrpParaRemover.Add(taxonomySubGroup.Key);
            }

            //Remove os subgrupos de taxonomia que estão vazios.
            foreach (string taxSubGrp in taxSubGrpParaRemover)
            {
                regrasAgrupadas.Remove(taxSubGrp);
            }
            taxSubGrpParaRemover.Clear();

            GC.Collect();
        }

        /// <summary>
        /// Encontra taxonomia de um determinado antecedente de uma determinada regra.
        /// </summary>
        /// <param name="regra"></param>
        /// <param name="indexAntecedente"></param>
        /// <returns></returns>
        string encontraTaxonomia(Rule regra, int indexAntecedente)
        {
            foreach (Taxonomy tax in taxonomias)
            {
                //Se a regra atual possui o antecedente especificado...
                if (indexAntecedente < regra.AntecedenteDesmembrado.Count)
                {
                    if (tax.pertence(regra.AntecedenteDesmembrado[indexAntecedente], null) != null)
                        return tax.cabeca.valor;
                }
            }

            return null;
        }

        /// <summary>
        /// Converte esta estrutura em um list comum.
        /// </summary>
        /// <returns></returns>
        public List<Rule> gerarListComum()
        {
            List<Rule> listComumRegras = new List<Rule>();

            //Para cada subgrupo de taxonomias...
            foreach (KeyValuePair<string, Dictionary<string, List<Rule>>> taxonomySubGroup in regrasAgrupadas)
            {
                //Para cada subgrupo de numeros de antecedentes...
                foreach (KeyValuePair<string, List<Rule>> numberSubGroup in taxonomySubGroup.Value)
                {
                    //Para cada regra existente neste subgrupo...
                    for (int i = 0; i < numberSubGroup.Value.Count; i++)
                    {
                        listComumRegras.Add(numberSubGroup.Value[i]);
                    }
                }
            }

            return listComumRegras;
        }
    }
}
